﻿using System.Collections.Generic;
using SpiceNet.Circuits;
using SpiceNet.Diagnostics;
using SpiceNet.Parameters;
using SpiceNet.Simulations;

namespace SpiceNet.Models
{
    /// <summary>
    /// A current controlled
    /// </summary>
    public class CurrentControlledVoltageSource : Instance
    {
        /// <summary>
        /// Get parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        #region Parameters
        private enum _c
        {
            /* device parameters */
            CCVS_TRANS = 1,
            CCVS_CONTROL = 2,
            CCVS_POS_NODE = 3,
            CCVS_NEG_NODE = 4,
            CCVS_BR = 5,
            CCVS_CONT_BR = 6,
            CCVS_TRANS_SENS = 7,
            CCVS_CURRENT = 8,
            CCVS_POWER = 9,
            CCVS_VOLTS = 10,

            /* device questions */
            CCVS_QUEST_SENS_REAL = 201,
            CCVS_QUEST_SENS_IMAG = 202,
            CCVS_QUEST_SENS_MAG = 203,
            CCVS_QUEST_SENS_PH = 204,
            CCVS_QUEST_SENS_CPLX = 205,
            CCVS_QUEST_SENS_DC = 206
        }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "gain", new IP(IF.IOPU, (int)_c.CCVS_TRANS, T.REAL, "Transresistance (gain)") },
            { "control", new IP(IF.IOPU, (int)_c.CCVS_CONTROL, T.INSTANCE, "Controlling voltage source") },
            { "sens_trans", new IP(IF.IP, (int)_c.CCVS_TRANS_SENS, T.FLAG, "flag to request sens. WRT transimpedance") },
            { "pos_node", new IP(IF.OPU, (int)_c.CCVS_POS_NODE, T.INTEGER, "Positive node of source") },
            { "neg_node", new IP(IF.OPU, (int)_c.CCVS_NEG_NODE, T.INTEGER, "Negative node of source") },
            { "i", new IP(IF.OP, (int)_c.CCVS_CURRENT, T.REAL, "CCVS output current") },
            { "v", new IP(IF.OP, (int)_c.CCVS_VOLTS, T.REAL, "CCVS output voltage") },
            { "p", new IP(IF.OP, (int)_c.CCVS_POWER, T.REAL, "CCVS power") },
            { "sens_dc", new IP(IF.OPU, (int)_c.CCVS_QUEST_SENS_DC, T.REAL, "dc sensitivity ") },
            { "sens_real", new IP(IF.OPU, (int)_c.CCVS_QUEST_SENS_REAL, T.REAL, "real part of ac sensitivity") },
            { "sens_imag", new IP(IF.OPU, (int)_c.CCVS_QUEST_SENS_IMAG, T.REAL, "imag part of ac sensitivity") },
            { "sens_mag", new IP(IF.OPU, (int)_c.CCVS_QUEST_SENS_MAG, T.REAL, "sensitivity of ac magnitude") },
            { "sens_ph", new IP(IF.OPU, (int)_c.CCVS_QUEST_SENS_PH, T.REAL, "sensitivity of ac phase") },
            { "sens_cplx", new IP(IF.OPU, (int)_c.CCVS_QUEST_SENS_CPLX, T.COMPLEX, "ac sensitivity") }
        };

        public Parameter<double> CCVScoeff { get; } = new Parameter<double>();
        public string CCVScontName { get; set; }
        public int CCVSsenParmNo { get; set; }
        #endregion

        /// <summary>
        /// Private variables
        /// </summary>
        private int CCVSposNode;
        private int CCVSnegNode;
        private int CCVSbranch;
        private int CCVScontBranch;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        public CurrentControlledVoltageSource(string name) : base(name, 2, typeof(CurrentControlledVoltageSourceModel)) { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the Current-Controlled Voltage source</param>
        /// <param name="nplus">The name of the positive output node</param>
        /// <param name="nminus">The name of the negative output node</param>
        /// <param name="vsource">The name of the voltage source through which the current is amplified</param>
        /// <param name="gain">The transimpedance gain (Ohm)</param>
        public CurrentControlledVoltageSource(string name, string nplus, string nminus, string vsource, double gain) : this(name)
        {
            Connect(nplus, nminus);
            CCVScontName = vsource;
            CCVScoeff.Par(gain);
        }

        /// <summary>
        /// Set a parameter
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.CCVS_TRANS:
                    CCVScoeff.Par((double)value);
                    break;
                case _c.CCVS_CONTROL:
                    CCVScontName = (string)value;
                    break;
                case _c.CCVS_TRANS_SENS:
                    CCVSsenParmNo = (int)value;
                    break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Ask a parameter
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.CCVS_TRANS:
                    return CCVScoeff.Value;
                case _c.CCVS_CONTROL:
                    return CCVScontName;
                case _c.CCVS_POS_NODE:
                    return CCVSposNode;
                case _c.CCVS_NEG_NODE:
                    return CCVSnegNode;
                case _c.CCVS_BR:
                    return CCVSbranch;
                case _c.CCVS_CONT_BR:
                    return CCVScontBranch;
                case _c.CCVS_CURRENT:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else
                    {
                        return ckt.State.Solution[CCVSbranch];
                    }
                case _c.CCVS_VOLTS:
                    return (ckt.State.Solution[CCVSposNode] -
                    ckt.State.Solution[CCVSnegNode]);
                case _c.CCVS_POWER:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else
                    {
                        return ckt.State.Solution[CCVSbranch]
                                * (ckt.State.Solution[CCVSposNode] -
                                ckt.State.Solution[CCVSnegNode]);
                    }
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Setup the instance
        /// </summary>
        /// <param name="ckt"></param>
        public override void Setup(Circuit ckt, Model model, object p = null)
        {
            var nodes = BindNodes(ckt, NodeType.Current);
            CCVSposNode = nodes[0].Number;
            CCVSnegNode = nodes[1].Number;
            CCVSbranch = nodes[2].Number;

            // Find the voltage source
            var models = ckt.FindModels(typeof(VoltageSourceModel));
            CCVScontBranch = -1;
            foreach (var mod in models)
            {
                VoltageSource vs = mod.Find(CCVScontName) as VoltageSource;
                if (vs != null)
                    CCVScontBranch = vs.VSRCbranch;
            }
            if (CCVScontBranch < 0)
                throw new CircuitException("Could not find controlling voltage source for " + Name);
        }

        /// <summary>
        /// Temperature dependent calculations
        /// </summary>
        /// <param name="ckt"></param>
        public override void Temperature(Circuit ckt, Model model, object p = null)
        {
        }

        /// <summary>
        /// Load the current controlled voltage source
        /// </summary>
        /// <param name="ckt"></param>
        public override void Load(Circuit ckt, Model model, object p = null)
        {
            CircuitState state = ckt.State;
            state.Matrix[CCVSposNode, CCVSbranch] += 1.0;
            state.Matrix[CCVSbranch, CCVSposNode] += 1.0;
            state.Matrix[CCVSnegNode, CCVSbranch] -= 1.0;
            state.Matrix[CCVSbranch, CCVSnegNode] -= 1.0;
            state.Matrix[CCVSbranch, CCVScontBranch] -= CCVScoeff;
        }

        /// <summary>
        /// Load the current controlled voltage
        /// </summary>
        /// <param name="ckt"></param>
        public override void AcLoad(Circuit ckt, Model model, object p = null)
        {
            CircuitStateComplex state = ckt.StateComplex;
            state.Matrix[CCVSposNode, CCVSbranch] += 1.0;
            state.Matrix[CCVSbranch, CCVSposNode] += 1.0;
            state.Matrix[CCVSnegNode, CCVSbranch] -= 1.0;
            state.Matrix[CCVSbranch, CCVSnegNode] -= 1.0;
            state.Matrix[CCVSbranch, CCVScontBranch] -= CCVScoeff.Value;
        }
    }
}
